package utils

import java.io.FileOutputStream
import java.security.cert.Certificate
import java.security.{ KeyStore, PrivateKey }

import com.itextpdf.text.pdf.PdfSignatureAppearance.RenderingMode
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard
import com.itextpdf.text.{ Image, Rectangle }
import com.itextpdf.text.pdf.{ PdfReader, PdfSignatureAppearance, PdfStamper }
import com.itextpdf.text.pdf.security._

import scala.util.Try

object SignPDF {

  val KEYSTORE = "/pdfResources/dlt.p12"
  val IMAGE_CLASSPATH = "/pdfResources/kunlunxiaohui.png"
  val PASSWORD: Array[Char] = "hs8871575".toCharArray

  def signPdf(inputFile: String, outputFile: String): Unit = {
    try { //读取keystore ，获得私钥和证书链
      val ks = KeyStore.getInstance("PKCS12")
      ks.load(classOf[CommonDB].getResourceAsStream(KEYSTORE), PASSWORD)
      val alias = ks.aliases.nextElement
      val pk = ks.getKey(alias, PASSWORD).asInstanceOf[PrivateKey]
      val chain = ks.getCertificateChain(alias)
      //new一个上边自定义的方法对象，调用签名方法
      //app.sign(SRC, String.format(DEST, 1), chain, pk, DigestAlgorithms.SHA1, provider.getName(), CryptoStandard.CMS, "Test 1", "Ghent");
      //app.sign(SRC, String.format(DEST, 2), chain, pk, "SM3", provider.getName(), CryptoStandard.CADES, "Test 2", "Ghent");
      //app.sign(inputFile, String.format(outputFile, 3), chain, pk, DigestAlgorithms.SHA1, null, CryptoStandard.CMS, "Test 3", "Ghent");
      sign(
        inputFile,
        String.format(outputFile, 3: java.lang.Integer),
        chain,
        pk,
        DigestAlgorithms.SHA1,
        null,
        CryptoStandard.CMS,
        "鹤山市昆仑学校",
        "广东省鹤山市")
      //app.sign(SRC, String.format(DEST, 4), chain, pk, DigestAlgorithms.RIPEMD160, provider.getName(), CryptoStandard.CADES, "Test 4", "Ghent");
    } catch {
      case e: Exception =>
        // TODO Auto-generated catch block
        //JOptionPane.showMessageDialog(null, e.getMessage());
        e.printStackTrace()
    }
  }

  /*public static Certificate[] getPublicCertificate(String path) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException {
      KeyStore ks = KeyStore.getInstance("PKCS12");
      ks.load(new FileInputStream(KEYSTORE), PASSWORD);
      String alias = (String)ks.aliases().nextElement();
      PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);
      Certificate[] chain = ks.getCertificateChain(alias);
      return chain;
  }*/

  def sign(
    src: String, //需要签章的pdf文件路径
    dest: String, // 签完章的pdf文件路径
    chain: Array[Certificate], //证书链
    pk: PrivateKey, //签名私钥
    digestAlgorithm: String, //摘要算法名称，例如SHA-1
    provider: String, // 密钥算法提供者，可以为null
    subfilter: MakeSignature.CryptoStandard, //数字签名格式，itext有2种
    reason: String, //签名的原因，显示在pdf签名属性中，随便填
    location: String //签名的地点，显示在pdf签名属性中，随便填
  ) = {
    //需要签章的pdf文件路径, dest: String// 签完章的pdf文件路径, chain: Array[Certificate]//证书链, pk: PrivateKey//签名私钥, digestAlgorithm: String//摘要算法名称，例如SHA-1, provider: String// 密钥算法提供者，可以为null, subfilter: MakeSignature.CryptoStandard//数字签名格式，itext有2种, reason: String//签名的原因，显示在pdf签名属性中，随便填, location: String): Unit =  { //签名的地点，显示在pdf签名属性中，随便填
    //下边的步骤都是固定的，照着写就行了，没啥要解释的
    // Creating the reader and the stamper，开始pdfreader
    var reader: PdfReader = null
    try {
      reader = new PdfReader(src)
      //目标文件输出流
      val os = new FileOutputStream(dest)
      //创建签章工具PdfStamper ，最后一个boolean参数
      //false的话，pdf文件只允许被签名一次，多次签名，最后一次有效
      //true的话，pdf可以被追加签名，验签工具可以识别出每次签名之后文档是否被修改
      val stamper: PdfStamper =
        PdfStamper.createSignature(reader, os, '\u0000', null, true)
      // 获取数字签章属性对象，设定数字签章的属性
      val appearance: PdfSignatureAppearance = stamper.getSignatureAppearance
      appearance.setReason(reason)
      appearance.setLocation(location)
      //设置签名的位置，页码，签名域名称，多次追加签名的时候，签名预名称不能一样
      //签名的位置，是图章相对于pdf页面的位置坐标，原点为pdf页面左下角
      //四个参数的分别是，图章左下角x，图章左下角y，图章右上角x，图章右上角y
      appearance.setVisibleSignature(
        new Rectangle(420, 540, 520, 740),
        1,
        "sign")
      //读取图章图片，这个image是itext包的image
      val image: Image = Image.getInstance(
        classOf[CommonDB].getResource(IMAGE_CLASSPATH).toURI.toURL)
      appearance.setSignatureGraphic(image)
      appearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED)
      //设置图章的显示方式，如下选择的是只显示图章（还有其他的模式，可以图章和签名描述一同显示）
      appearance.setRenderingMode(RenderingMode.GRAPHIC)
      // 这里的itext提供了2个用于签名的接口，可以自己实现，后边着重说这个实现
      // 摘要算法
      val digest = new BouncyCastleDigest
      // 签名算法
      val signature = new PrivateKeySignature(pk, digestAlgorithm, null)
      // 调用itext签名方法完成pdf签章
      MakeSignature.signDetached(
        appearance,
        digest,
        signature,
        chain,
        null,
        null,
        null,
        0,
        subfilter)
    } finally {
      if (reader != null)
        Try {
          reader.close()
        }.fold(err => err.printStackTrace, (a: Unit) => a)
    }
  }

  /*public static void manipulatePdf(String dest) throws Exception {
    Security.addProvider(new BouncyCastleProvider());

    Certificate[] cert = getPublicCertificate(PUBLIC);
    PdfWriter writer = new PdfWriter(DEST, new WriterProperties()
            .setPublicKeyEncryption(
                    cert,
                    new int[]{EncryptionConstants.ALLOW_PRINTING},
                    EncryptionConstants.STANDARD_ENCRYPTION_40));

    PdfDocument pdfDoc = new PdfDocument(writer);
    Document doc = new Document(pdfDoc);
    doc.add(new Paragraph("My secret hello"));
    doc.close();
}*/
}
